package com.huirui.server.service;

import cn.hutool.core.bean.BeanUtil;
import com.huirui.server.pojo.vo.DiskInfoVo;
import com.huirui.server.pojo.vo.SystemMonitoringDataVo;
import com.huirui.server.util.SystemInfoUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HardwareAbstractionLayer;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.RuntimeMXBean;
import java.text.DecimalFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;


@Slf4j
@Service
public class SystemMonitoringService {
    // 采样间隔（毫秒），建议 500-2000ms，平衡实时性和精度
    private static final int SAMPLING_INTERVAL = 1000;
    private static final DecimalFormat DF = new DecimalFormat("0.00");
    // 单位转换常量（1GB = 1024^3 Byte，1MB = 1024^2 Byte）
    private static final long GB = 1024L * 1024 * 1024;
    private static final long MB = 1024L * 1024;
    private static final DateTimeFormatter DATE_TIME_FORMATTER =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private static final SystemInfo systemInfo = new SystemInfo();
    public static final HardwareAbstractionLayer hardware = systemInfo.getHardware();
    public static final CentralProcessor processor = hardware.getProcessor();

    @SneakyThrows
    public SystemMonitoringDataVo getSystemMonitorData() {
        SystemMonitoringDataVo systemMonitoringDataVo = new SystemMonitoringDataVo();
        // CPU核心数
        systemMonitoringDataVo.setCpuCoreCount(processor.getLogicalProcessorCount());
        // 系统内存
        GlobalMemory memory = hardware.getMemory();
        long totalMemory = memory.getTotal();       // 总内存
        long availableMemory = memory.getAvailable(); // 剩余内存
        long usedMemory = totalMemory - availableMemory; // 已用内存
        double usageRate = totalMemory == 0 ? 0.0 : (double) usedMemory / totalMemory * 100; // 内存使用率
        String totalMemStr = formatMemory(totalMemory);
        String usedMemStr = formatMemory(usedMemory);
        String availableMemStr = formatMemory(availableMemory);
        systemMonitoringDataVo.setSystemMemoryUsageRate(DF.format(usageRate));
        systemMonitoringDataVo.setSystemUsedMemory(usedMemStr);
        systemMonitoringDataVo.setSystemTotalMemory(totalMemStr);
        systemMonitoringDataVo.setSystemRemainingMemory(availableMemStr);
        // JVM 内存
        MemoryMXBean memoryMXBean = java.lang.management.ManagementFactory.getMemoryMXBean();
        MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage();
        long heapUsed = heapUsage.getUsed();       // 堆已用内存（字节）
        long heapCommitted = heapUsage.getCommitted(); // 堆当前已分配的内存（字节，<= max）
        MemoryUsage nonHeapUsage = memoryMXBean.getNonHeapMemoryUsage();
        long nonHeapUsed = nonHeapUsage.getUsed();
        long nonHeapCommitted = nonHeapUsage.getCommitted();
        long totalCommitted = heapCommitted + nonHeapCommitted; // JVM 总已分配内存
        long totalUsed = heapUsed + nonHeapUsed;                // JVM 总已用内存
        long totalFree = totalCommitted - totalUsed;            // JVM 总剩余内存
        double JVMUsageRate = (totalUsed * 1.0 / totalCommitted) * 100; // 内存占用率
        systemMonitoringDataVo.setJVMMemoryUsageRate(DF.format(JVMUsageRate));
        systemMonitoringDataVo.setJVMTotalMemory(formatMemory(totalCommitted));
        systemMonitoringDataVo.setJVMUsedMemory(formatMemory(totalUsed));
        systemMonitoringDataVo.setJVMRemainingMemory(formatMemory(totalFree));
        // 虚拟机名称
        systemMonitoringDataVo.setJavaName(System.getProperty("java.vm.name"));
        // 虚拟币版本
        systemMonitoringDataVo.setJavaVersion(System.getProperty("java.vm.version"));
        // 启动时间
        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
        long startTimeMillis = runtimeMXBean.getStartTime();
        long currentTimeMillis = System.currentTimeMillis();
        long uptimeMillis = currentTimeMillis - startTimeMillis;
        systemMonitoringDataVo.setStartupTime(
                Instant.ofEpochMilli(startTimeMillis) // 毫秒转 Instant（UTC）
                        .atZone(ZoneId.of("Asia/Shanghai")) // 转为北京时区的带时区时间
                        .toLocalDateTime() // 提取本地日期时间
        );
        // 系统已运行时间
        systemMonitoringDataVo.setSystemRunningTime(formatTimestampToDaysHoursMinutes(uptimeMillis));
        // 安装路径
        systemMonitoringDataVo.setInstallationPath(System.getProperty("java.home"));
        // 项目路径
        systemMonitoringDataVo.setProjectPath(SystemInfoUtil.getSystemInfo().getUserDir());
        // 服务器名称
        systemMonitoringDataVo.setServerName(SystemInfoUtil.getSystemInfo().getHostName());
        // 操作系统
        systemMonitoringDataVo.setOperatingSystem(SystemInfoUtil.getSystemInfo().getOs());
        // 系统架构
        systemMonitoringDataVo.setSystemArchitecture(SystemInfoUtil.getSystemInfo().getOsArch());
        // IP地址
        systemMonitoringDataVo.setServerIp(SystemInfoUtil.getSystemInfo().getHost());
        // 磁盘信息
        List<DiskInfoVo> diskInfoList = new ArrayList<>();
        SystemInfoUtil
                .getOSRuntimeInfo()
                .getDisksList()
                .forEach(diskInfo -> {
                    DiskInfoVo diskInfoVo = BeanUtil.copyProperties(diskInfo, DiskInfoVo.class);
                    diskInfoList.add(diskInfoVo);
                });
        systemMonitoringDataVo.setDiskInfoList(diskInfoList);

        return systemMonitoringDataVo;
    }

    /**
     * 工具方法：格式化内存大小（自动适配 GB/MB 单位，小于 1GB 用 MB，否则用 GB）
     *
     * @param bytes 内存字节数
     * @return 格式化后的字符串（如 "15.52 GB"、"896.30 MB"）
     */
    private static String formatMemory(long bytes) {
        if (bytes >= GB) {
            return DF.format((double) bytes / GB) + " GB";
        } else {
            return DF.format((double) bytes / MB) + " MB";
        }
    }

    /**
     * 时间戳转换为 xx天xx时xx分钟 格式
     *
     * @param timestamp 毫秒级时间戳（非负整数）
     * @return 格式化后的时间字符串，例如："3天12时30分钟"、"0分钟"、"5时20分钟"
     * @throws IllegalArgumentException 若输入时间戳为负数
     */
    public static String formatTimestampToDaysHoursMinutes(long timestamp) {
        // 校验时间戳合法性（非负）
        if (timestamp < 0) {
            throw new IllegalArgumentException("时间戳不能为负数");
        }

        // 定义时间单位常量（毫秒转换）
        final long MINUTE = 60 * 1000;    // 1分钟 = 60000毫秒
        final long HOUR = 60 * MINUTE;    // 1小时 = 3600000毫秒
        final long DAY = 24 * HOUR;       // 1天 = 86400000毫秒

        // 计算天、时、分（向下取整，忽略秒数）
        long days = timestamp / DAY;
        long remainingAfterDays = timestamp % DAY;

        long hours = remainingAfterDays / HOUR;
        long remainingAfterHours = remainingAfterDays % HOUR;

        long minutes = remainingAfterHours / MINUTE;

        // 拼接结果（只保留非零部分，避免"0天0时0分钟"等冗余格式）
        StringBuilder result = new StringBuilder();

        if (days > 0) {
            result.append(days).append("天");
        }
        if (hours > 0 || (days > 0 && minutes > 0)) { // 有天且有分钟时，即使小时为0也显示"0时"（可选，可根据需求调整）
            result.append(hours).append("时");
        }
        // 确保至少显示分钟（包括0分钟的情况）
        result.append(minutes).append("分钟");

        return result.toString();
    }

    @SneakyThrows
    public SystemMonitoringDataVo getCPUMonitorData() {
        SystemMonitoringDataVo systemMonitoringDataVo = new SystemMonitoringDataVo();
        // COU使用率
        long[] prevTicks = processor.getSystemCpuLoadTicks();
        Thread.sleep(SAMPLING_INTERVAL);
        long[] currTicks = processor.getSystemCpuLoadTicks();
        long userDiff = currTicks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
        long systemDiff = currTicks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
        long idleDiff = currTicks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
        long totalDiff = userDiff + systemDiff + idleDiff
                + (currTicks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()])
                + (currTicks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()])
                + (currTicks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()])
                + (currTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()])
                + (currTicks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()]);
        double userUsage = totalDiff == 0 ? 0.0 : (double) userDiff / totalDiff * 100;
        double systemUsage = totalDiff == 0 ? 0.0 : (double) systemDiff / totalDiff * 100;
        double idleUsage = totalDiff == 0 ? 0.0 : (double) idleDiff / totalDiff * 100;
        systemMonitoringDataVo.setUserCpuUsageRate(DF.format(userUsage));
        systemMonitoringDataVo.setSystemCpuUsageRate(DF.format(systemUsage));
        systemMonitoringDataVo.setCurrentCpuIdleRate(DF.format(idleUsage));
        return systemMonitoringDataVo;
    }
}
